﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using jade.core;
using TravelLib;
using jade.core.behaviours;
using jade.lang.acl;
using System.Windows.Forms;

namespace MultiAgentLib
{
    public class PersonalAgent : Agent
    {
        private Request _userRequest;
        public AgentForm Form
        {
            get;
            private set;
        }
        private RichTextBox _messageBox;
        private List<Flight> _preFilterdFlights = new List<Flight>();
        private List<Flight> _usableFlights = new List<Flight>();
        private List<Journey> _journeys = new List<Journey>();
        public List<Flight> UsableFlights
        {
            get { return _usableFlights; }
            set { _usableFlights = value; }
        }

        public Request UserRequest
        {
            get { return _userRequest; }
        }
        public int IndexOfIntermediateRoute;
        private List<AID> _providerAgents;

        public List<AID> ProviderAgents
        {
            get { return _providerAgents; }
        }
        private ConsoleColor _color;

        public override void setup()
        {
            object[] args = this.getArguments();
            _color = Color.GetColor();
            if (args != null)
            {
                _userRequest = args[0] as Request;
                Form = args[1] as AgentForm;
                _messageBox = args[2] as RichTextBox;
                Form.Show();
                Form.AddTextLine("Personal Agent" + " waiting for requests ");
            }
            else
            {
                _userRequest = null;
            }
            FindSolverServices();
            addBehaviour(new WinFormRefreshBehaviour(this, 100));
            addBehaviour(new AskOffersBehaviour(this));
            addBehaviour(new ReceiveOffersBehaviour(this));
        }

        private void FindSolverServices()
        {
            WriteConsole("Waiting for provider...");
            try
            {
                _providerAgents = YellowPages.FindService("Services.Company", String.Empty, this, 100000000);
                // if it is null it raises an exception in getName()
                foreach (AID aid in _providerAgents)
                {
                    WriteConsole(" Agent found: " + aid.getName() + "\r\n");
                }
            }
            catch (Exception ex)
            {
                WriteConsole("Exception finding provider" + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace);
            }
        }

        public void WriteConsole(string s)
        {
            Console.BackgroundColor = _color;
            Console.ForegroundColor = ConsoleColor.Black;
            Console.WriteLine("[" + getLocalName() + "]: " + s);
            Form.AddTextLine(s);
        }

        public void UpdateClient()
        {
            foreach (Journey j in _journeys.Where(x => x.IsNew))
            {
                string newValue = j.ToString(this._userRequest.NumberOfSeats, this._userRequest.TravelClass);
                j.IsNew = false;
                if (_messageBox.InvokeRequired)
                {
                    _messageBox.Invoke((MethodInvoker)delegate { _messageBox.AppendText(newValue); });
                }
                else
                {
                    _messageBox.AppendText(newValue);

                }
            }
        }
        internal void AddAndFilterFlights(List<FlightWrapper> flightList)
        {
            TravelClassEnum en = TravelClassEnum.BUSSINESS_CLASS;
             switch (StringEnum.GetStringValue(_userRequest.TravelClass))
                {
                    case "FIRST CLASS": en = TravelClassEnum.FIRST_CLASS;
                        break;
                    case "BUSSINESS CLASS": en = TravelClassEnum.BUSSINESS_CLASS;
                        break;
                    case "ECONOMY CLASS": en = TravelClassEnum.ECONOMY_CLASS;
                        break;
                }
            if (!_userRequest.StopOver && !_userRequest.PlaneChange)
            {
                foreach(FlightWrapper f in flightList)
                {
                    List<Flight> ff = new List<Flight>();
                    Flight fff =new Flight(f);
                    if(fff.TravelClasses.Any(x=>x.TravelClass == en && x.SeatsAvailable >=_userRequest.NumberOfSeats))
                    {
                        ff.Add(fff);
                        _journeys.Add(new Journey()
                        {
                            ActualRoute = _userRequest.UserSetRoute,
                            Flights = ff,
                            IsNew = true,
                        });
                    }
                }
                return;
            }

            foreach (FlightWrapper f in flightList)
            {
                 Flight fff =new Flight(f);
                 if(fff.TravelClasses.Any(x=>x.TravelClass == en && x.SeatsAvailable >=_userRequest.NumberOfSeats))
                         _usableFlights.Add(fff);
            }
            if (_userRequest.StopOver) //same companies
            {
                // adauga toate rutele doar daca nu se schimba compania
                foreach (HashSet<Route> lr in _userRequest.PreIntermediateRoutes)
                {
                    foreach (Company c in CompanyManager.Companies)
                    {
                        bool canMakeJourney = true;
                        foreach (Route r in lr)
                        {
                            if (!_usableFlights.Any(x => x.Company.CompanyName == c.CompanyName && x.Route.IsEqual(r)))
                            {
                                canMakeJourney = false;
                                break;
                            }
                        }
                        if (canMakeJourney)
                        {
                            List<Flight> ff = new List<Flight>();
                            foreach (Route r in lr)
                            {
                                ff.Add(_usableFlights.First(x => x.Company.CompanyName == c.CompanyName && x.Route.IsEqual(r)));
                            }
                            _journeys.Add(new Journey()
                           {
                               ActualRoute = _userRequest.UserSetRoute,
                               Flights = ff,
                               IsNew = true,
                           });
                        }

                    }
                }
                return;
            }
            if (_userRequest.PlaneChange)//diff companies
            {
                //adauga ruta directa + rutele compuse de 2 companii diferita, dar nu si stopoverurile
                 foreach (HashSet<Route> lr in _userRequest.PreIntermediateRoutes)
                {
                        bool canMakeJourney = true;
                        foreach (Route r in lr)
                        {
                            if (!_usableFlights.Any(x => x.Route.IsEqual(r)))
                            {
                                canMakeJourney = false;
                                break;  
                            }
                        }
                        if(canMakeJourney)
                        {
                            List<Flight> ff = new List<Flight>();
                            foreach (Route r in lr)
                            {
                                ff.Add (_usableFlights.First(x=>x.Route.IsEqual(r)));

                            }
                             _journeys.Add(new Journey()
                            {
                                ActualRoute = _userRequest.UserSetRoute,
                                Flights = ff,
                                IsNew = true,
                            });
                        }


                }
                return;
            }
            if (_userRequest.StopOver && _userRequest.PlaneChange)
            {
                //ia toate rutele din routeList 
                //invalid combination
            }

          //  _usableFlights.AddRange(flightList);
        }


        public class AskOffersBehaviour : SimpleBehaviour
        {
            private new PersonalAgent myAgent;

            public AskOffersBehaviour(PersonalAgent a) : base(a) { myAgent = a; }

            public override void action()
            {
                if (myAgent.UserRequest.IntermediateRoutes == null || myAgent.UserRequest.IntermediateRoutes.Count == 0)
                    return;
                RouteWrapper askingRoute = new RouteWrapper(myAgent.UserRequest.IntermediateRoutes.ElementAt(myAgent.IndexOfIntermediateRoute));

                foreach (AID receiverAid in myAgent.ProviderAgents)
                {
                    ACLMessage message = new ACLMessage(ACLMessage.REQUEST);
                    message.addReceiver(receiverAid);
                    message.setConversationId("ASK");
                    message.setContent(Utils.SerializeObject<RouteWrapper>(askingRoute));
                    myAgent.send(message);
                }
                myAgent.IndexOfIntermediateRoute++;
            }

            public override bool done()
            {
                if (myAgent.IndexOfIntermediateRoute >= myAgent.UserRequest.IntermediateRoutes.Count)
                {
                    return true;
                }
                return false;
            }
        }

        public class ReceiveOffersBehaviour : CyclicBehaviour
        {
            private new PersonalAgent myAgent;

            public ReceiveOffersBehaviour(PersonalAgent a) : base(a) { myAgent = a; }

            public override void action()
            {
                MessageTemplate pattern1 = MessageTemplate.MatchConversationId("OFFER");//MessageTemplate.MatchInReplyTo(myAgent.getLocalName());
                ACLMessage message1 = myAgent.receive(pattern1);
                if (message1 != null)
                {
                    myAgent.WriteConsole("Personal Agent" + " receives flight list from " + message1.getSender().getLocalName());

                    List<FlightWrapper> flightList = Utils.DeSerializeObject<List<FlightWrapper>>(message1.getContent());
                    myAgent.WriteConsole(myAgent.getLocalName() + " received offers" + flightList.Count());
                    myAgent.AddAndFilterFlights(flightList);
                }
                else
                {
                    // myAgent.WriteConsole(myAgent.getLocalName() + " received invalid message ");
                }
            }
        }

        internal class WinFormRefreshBehaviour : TickerBehaviour
        {
            private new PersonalAgent myAgent;
            public WinFormRefreshBehaviour(PersonalAgent a, long period) : base(a, period) { myAgent = a; }
            public override void onTick()
            {
                myAgent.UpdateClient();
                myAgent.Form.DoEvents();
            }
        }
    }
}
